package com.ruyuan.payment.server.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.easysdk.payment.common.models.AlipayDataDataserviceBillDownloadurlQueryResponse;
import com.ruyuan.payment.server.alipay.AliPayService;
import com.ruyuan.payment.server.domain.*;
import com.ruyuan.payment.server.enums.BillStatusEnum;
import com.ruyuan.payment.server.enums.OrderPayChannelEnum;
import com.ruyuan.payment.server.enums.OrderPayStatusEnum;
import com.ruyuan.payment.server.exception.BusinessException;
import com.ruyuan.payment.server.exception.BusinessExceptionEnum;
import com.ruyuan.payment.server.mapper.BillAlipayMapper;
import com.ruyuan.payment.server.mapper.BillBatchMapper;
import com.ruyuan.payment.server.mapper.BillWxpayMapper;
import com.ruyuan.payment.server.mapper.OrderPayMapper;
import com.ruyuan.payment.server.util.DateTimeUtil;
import com.ruyuan.payment.server.util.FileUtil;
import com.ruyuan.payment.server.util.SnowFlake;
import com.ruyuan.payment.server.wxpay.WxPayService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;

@Service
public class BillService {

    private static final Logger LOG = LoggerFactory.getLogger(BillService.class);

    @Resource
    private AliPayService aliPayService;

    @Resource
    private SnowFlake snowFlake;

    @Resource
    private BillBatchMapper billBatchMapper;

    @Resource
    private BillAlipayMapper billAlipayMapper;

    @Resource
    private OrderPayMapper orderPayMapper;

    @Resource
    private BillHandleService billHandleService;

    @Value("${bill.path}")
    private String billPath;

    @Resource
    private WxPayService wxPayService;

    @Resource
    private BillWxpayMapper billWxpayMapper;

    public void doBillAliPay(String billDateStr) {
        try {
            LOG.info("初始化支付宝对账批次数据开始");
            BillBatch billBatch = initBatch(billDateStr, OrderPayChannelEnum.ALIPAY.getCode());

            LOG.info("获取支付宝对账文件并保存数据开始");
            getBillDataAliPay(billDateStr, billBatch);

            LOG.info("支付宝对账开始");
            checkBillAliPay(billDateStr, billBatch);

        } catch (Exception e) {
            LOG.error("对账异常", e);
            throw new BusinessException(BusinessExceptionEnum.BILL_ERROR);
        }
    }

    private void getBillDataAliPay(String billDate, BillBatch billBatch) throws Exception {
        String billPathDate = billPath + billDate + "A/";
        FileUtil.mkdirs(billPathDate);
        String zipFile = billPathDate + billDate + ".zip";

        // 判断账务明细.csv是否存在
        boolean isExist = false;
        File billPathDir = new File(billPathDate);
        File[] files = billPathDir.listFiles();
        for (File file : files) {
            if (file.getName().contains("账务明细.csv")) {
                isExist = true;
                break;
            }
        }

        if (!isExist) {
            LOG.info("下载对账单");
            AlipayDataDataserviceBillDownloadurlQueryResponse downloadResponse = aliPayService.downloadBill(billDate);

            LOG.info("获取下载地址");
            String url = downloadResponse.getBillDownloadUrl();

            LOG.info("开始下载，zipFile：{}", zipFile);
            FileUtil.downloadNet(url, zipFile);

            LOG.info("解压到指定目录");
            FileUtil.unZip(zipFile, billPathDate);
        } else {
            LOG.info("对账文件已存在。");
        }

        LOG.info("读文件");
        readAndSaveAliPay(billPathDate, billBatch);
    }

    public BillBatch initBatch(String billDateStr, String channel) {
        Date now = new Date();
        // 20220410A
        String batchNo = billDateStr.replaceAll("-", "") + channel;
        BillBatchExample billBatchExample = new BillBatchExample();
        billBatchExample.createCriteria().andBatchNoEqualTo(batchNo);
        List<BillBatch> billBatcheList = billBatchMapper.selectByExample(billBatchExample);

        if (CollectionUtils.isEmpty(billBatcheList)) {
            Date date = DateTimeUtil.parse(billDateStr, "yyyy-MM-dd");
            BillBatch billBatch = new BillBatch();
            billBatch.setId(snowFlake.nextId());
            billBatch.setBatchNo(batchNo);
            billBatch.setBillDate(date);
            billBatch.setChannel(channel);
            billBatch.setCreatedTime(now);
            billBatch.setUpdatedTime(now);
            billBatchMapper.insert(billBatch);
            return billBatch;
        } else {
            BillBatch billBatchDB = billBatcheList.get(0);
            billBatchDB.setUpdatedTime(now);
            billBatchMapper.updateByPrimaryKey(billBatchDB);
            return billBatchDB;
        }
    }

    private void readAndSaveAliPay(String billPathDate, BillBatch billBatch) throws Exception {
        LOG.info("清空当天的支付宝对账数据");
        BillAlipayExample billAlipayExample = new BillAlipayExample();
        billAlipayExample.createCriteria().andBatchNoEqualTo(billBatch.getBatchNo());
        billAlipayMapper.deleteByExample(billAlipayExample);

        File billPathDir = new File(billPathDate);
        File[] files = billPathDir.listFiles();
        for (File file : files) {
            if (file.getName().contains("账务明细.csv")) {
                BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file.getAbsoluteFile()), "gbk"));
                reader.readLine();
                reader.readLine();
                reader.readLine();
                reader.readLine();
                reader.readLine();
                while (true) {
                    String line = reader.readLine();
                    if (line.contains("账务明细列表结束")) {
                        break;
                    }
                    LOG.info("line: {}", line);
                    saveDetailAlipay(line, billBatch);
                }
            }
        }
    }

    public void saveDetailAlipay(String line, BillBatch billBatch) {
        Date now = new Date();
        String[] dataArray = line.split(",");
        BillAlipay billAlipay = new BillAlipay();
        billAlipay.setId(snowFlake.nextId());
        billAlipay.setBatchNo(billBatch.getBatchNo());
        billAlipay.setBillDate(billBatch.getBillDate());
        billAlipay.setChannel(billBatch.getChannel());
        billAlipay.setFinanceNo(dataArray[0].trim());
        billAlipay.setBusinessNo(dataArray[1].trim());
        billAlipay.setPayNo(dataArray[2].trim());
        billAlipay.setDesc(dataArray[3].trim());
        billAlipay.setChannelTime(DateTimeUtil.parse(dataArray[4].trim(), "yyyy-MM-dd HH:mm:ss"));
        billAlipay.setAccount(dataArray[5].trim());
        billAlipay.setAmountIn(new BigDecimal(dataArray[6].trim()));
        billAlipay.setAmountOut(new BigDecimal(dataArray[7].trim()));
        billAlipay.setAmountRemain(new BigDecimal(dataArray[8].trim()));
        billAlipay.setChannelName(dataArray[9].trim());
        billAlipay.setBusinessType(dataArray[10].trim());
        billAlipay.setRemark(dataArray[11].trim());
        billAlipay.setBillStatus(BillStatusEnum.INIT.getCode());
        billAlipay.setCreatedTime(now);
        billAlipay.setUpdatedTime(now);
        billAlipayMapper.insert(billAlipay);
    }

    public void checkBillAliPay(String billDateStr, BillBatch billBatch) {
        LOG.info("支付宝正向对账开始");
        checkBillAliPayA(billDateStr, billBatch);

        LOG.info("支付宝反向对账开始");
        checkBillAliPayB(billDateStr, billBatch);
    }

    private void checkBillAliPayA(String billDateStr, BillBatch billBatch) {
        Date dateStart = DateTimeUtil.parse(billDateStr, "yyyy-MM-dd");
        Date dateEnd = DateTimeUtil.parse(billDateStr + " 23:59:59.999", "yyyy-MM-dd HH:mm:ss.SSS");
        Date dateAliPayStart = DateTimeUtil.afterDays(dateStart, -10);
        Date dateAliPayEnd = DateTimeUtil.afterDays(dateStart, 10);
        OrderPayExample orderPayExample = new OrderPayExample();
        orderPayExample.or().andChannelTimeBetween(dateStart, dateEnd)
                .andStatusEqualTo(OrderPayStatusEnum.SUCCESS.getCode())
                .andChannelEqualTo(OrderPayChannelEnum.WXPAY.getCode())
                .andBillStatusNotEqualTo(BillStatusEnum.NORMAL.getCode());
        orderPayExample.or().andChannelTimeBetween(dateStart, dateEnd)
                .andStatusEqualTo(OrderPayStatusEnum.PART_REFUND.getCode())
                .andChannelEqualTo(OrderPayChannelEnum.WXPAY.getCode())
                .andBillStatusNotEqualTo(BillStatusEnum.NORMAL.getCode());
        orderPayExample.or().andChannelTimeBetween(dateStart, dateEnd)
                .andStatusEqualTo(OrderPayStatusEnum.REFUND.getCode())
                .andChannelEqualTo(OrderPayChannelEnum.WXPAY.getCode())
                .andBillStatusNotEqualTo(BillStatusEnum.NORMAL.getCode());
        List<OrderPay> orderPayList = orderPayMapper.selectByExample(orderPayExample);

        for (OrderPay orderPayDB : orderPayList) {
            LOG.info("支付订单数据：{}", orderPayDB);
            String payNo = orderPayDB.getPayNo();
            BillAlipayExample billAlipayExample = new BillAlipayExample();
            billAlipayExample.createCriteria().andPayNoEqualTo(payNo)
                    .andChannelTimeBetween(dateAliPayStart, dateAliPayEnd)
                    .andBusinessTypeEqualTo("在线支付");
            List<BillAlipay> billAlipayList = billAlipayMapper.selectByExample(billAlipayExample);
            if (CollectionUtils.isEmpty(billAlipayList)) {
                LOG.warn("对账结果短款：{}", orderPayDB);
                billHandleService.doWrongBillAliPay(billBatch, orderPayDB, null, BillStatusEnum.SHORT.getCode());
            } else {
                BillAlipay billAlipayDB = billAlipayList.get(0);
                LOG.info("查询到的支付宝对账数据：{}", billAlipayDB);
                if (orderPayDB.getChannelAmount().compareTo(billAlipayDB.getAmountIn()) == 0) {
                    LOG.info("对账结果正常，payNo：{}", payNo);
                    billHandleService.doRightBillAliPay(billBatch, orderPayDB, billAlipayDB);
                } else {
                    LOG.warn("对账结果金额错误：{}", orderPayDB);
                    billHandleService.doWrongBillAliPay(billBatch, orderPayDB, billAlipayDB, BillStatusEnum.ERROR.getCode());
                }
            }
        }
    }

    private void checkBillAliPayB(String billDateStr, BillBatch billBatch) {
        Date dateStart = DateTimeUtil.parse(billDateStr, "yyyy-MM-dd");
        Date dateOrderPayStart = DateTimeUtil.afterDays(dateStart, -10);
        Date dateOrderPayEnd = DateTimeUtil.afterDays(dateStart, 10);

        BillAlipayExample billAlipayExample = new BillAlipayExample();
        billAlipayExample.createCriteria().andBatchNoEqualTo(billBatch.getBatchNo())
                .andBusinessTypeEqualTo("在线支付")
                .andBillStatusNotEqualTo(BillStatusEnum.NORMAL.getCode());
        List<BillAlipay> billAlipayList = billAlipayMapper.selectByExample(billAlipayExample);

        for (BillAlipay billAlipayDB : billAlipayList) {
            LOG.info("支付宝订单数据：{}", billAlipayDB);
            String payNo = billAlipayDB.getPayNo();
            OrderPayExample orderPayExample = new OrderPayExample();
            orderPayExample.or().andPayNoEqualTo(payNo)
                    .andStatusEqualTo(OrderPayStatusEnum.SUCCESS.getCode())
                    .andChannelTimeBetween(dateOrderPayStart, dateOrderPayEnd);
            orderPayExample.or().andPayNoEqualTo(payNo)
                    .andStatusEqualTo(OrderPayStatusEnum.PART_REFUND.getCode())
                    .andChannelTimeBetween(dateOrderPayStart, dateOrderPayEnd);
            orderPayExample.or().andPayNoEqualTo(payNo)
                    .andStatusEqualTo(OrderPayStatusEnum.REFUND.getCode())
                    .andChannelTimeBetween(dateOrderPayStart, dateOrderPayEnd);
            List<OrderPay> orderPayList = orderPayMapper.selectByExample(orderPayExample);
            if (CollectionUtils.isEmpty(orderPayList)) {
                LOG.warn("对账结果长款：{}", billAlipayDB);
                billHandleService.doWrongBillAliPay(billBatch, null, billAlipayDB, BillStatusEnum.LONG.getCode());
            } else {
                OrderPay orderPayDB = orderPayList.get(0);
                LOG.info("查询到的本地订单对账数据：{}", orderPayDB);
                if (orderPayDB.getChannelAmount().compareTo(billAlipayDB.getAmountIn()) == 0) {
                    LOG.info("对账结果正常，payNo：{}", payNo);
                    billHandleService.doRightBillAliPay(billBatch, orderPayDB, billAlipayDB);
                } else {
                    LOG.warn("对账结果金额错误：{}", orderPayDB);
                    billHandleService.doWrongBillAliPay(billBatch, orderPayDB, billAlipayDB, BillStatusEnum.ERROR.getCode());
                }
            }
        }
    }

    public void doBillWxPay(String billDateStr) {
        try {
            LOG.info("初始化微信对账批次数据开始");
            BillBatch billBatch = initBatch(billDateStr, OrderPayChannelEnum.WXPAY.getCode());

            LOG.info("获取微信对账文件并保存数据开始");
            getBillDataWxPay(billDateStr, billBatch);

            LOG.info("微信对账开始");
            checkBillWxPay(billDateStr, billBatch);

        } catch (Exception e) {
            LOG.error("对账异常", e);
            throw new BusinessException(BusinessExceptionEnum.BILL_ERROR);
        }
    }

    private void getBillDataWxPay(String billDate, BillBatch billBatch) throws Exception {
        String result = wxPayService.tradeBill(billDate);
        JSONObject jsonObject = JSON.parseObject(result);
        String downloadUrl = jsonObject.getString("download_url");
        String[][] data = wxPayService.downloadBill(downloadUrl);
        LOG.info("微信对账数据条数：{}", data.length);

        LOG.info("清空当天的微信对账数据");
        BillWxpayExample billWxpayExample = new BillWxpayExample();
        billWxpayExample.createCriteria().andBatchNoEqualTo(billBatch.getBatchNo());
        billWxpayMapper.deleteByExample(billWxpayExample);

        saveDetailWxpay(data, billBatch);
    }

    public void saveDetailWxpay(String[][] data, BillBatch billBatch) {
        Date now = new Date();
        for (int i = 0; i < data.length; i++) {
            String[] dataArray = data[i];
            BillWxpay billWxpay = new BillWxpay();
            billWxpay.setId(snowFlake.nextId());
            billWxpay.setBatchNo(billBatch.getBatchNo());
            billWxpay.setBillDate(billBatch.getBillDate());
            billWxpay.setChannel(billBatch.getChannel());
            billWxpay.setBusinessNo(dataArray[5].trim());
            billWxpay.setPayNo(dataArray[6].trim());
            billWxpay.setDesc(dataArray[20].trim());
            billWxpay.setChannelTime(DateTimeUtil.parse(dataArray[0].trim(), "yyyy-MM-dd HH:mm:ss"));
            billWxpay.setAmountIn(new BigDecimal(dataArray[12].trim()));
            billWxpay.setAmountOut(new BigDecimal(dataArray[16].trim()));
            billWxpay.setChannelStatus(dataArray[9].trim());
            billWxpay.setBillStatus(BillStatusEnum.INIT.getCode());
            billWxpay.setCreatedTime(now);
            billWxpay.setUpdatedTime(now);
            billWxpayMapper.insert(billWxpay);
        }
    }

    public void checkBillWxPay(String billDateStr, BillBatch billBatch) {
        LOG.info("微信正向对账开始");
        checkBillWxPayA(billDateStr, billBatch);

        LOG.info("微信反向对账开始");
        checkBillWxPayB(billDateStr, billBatch);
    }

    private void checkBillWxPayA(String billDateStr, BillBatch billBatch) {
        Date dateStart = DateTimeUtil.parse(billDateStr, "yyyy-MM-dd");
        Date dateEnd = DateTimeUtil.parse(billDateStr + " 23:59:59.999", "yyyy-MM-dd HH:mm:ss.SSS");
        Date dateWxPayStart = DateTimeUtil.afterDays(dateStart, -10);
        Date dateWxPayEnd = DateTimeUtil.afterDays(dateStart, 10);
        OrderPayExample orderPayExample = new OrderPayExample();
        orderPayExample.or().andChannelTimeBetween(dateStart, dateEnd)
                .andStatusEqualTo(OrderPayStatusEnum.SUCCESS.getCode())
                .andChannelEqualTo(OrderPayChannelEnum.WXPAY.getCode())
                .andBillStatusNotEqualTo(BillStatusEnum.NORMAL.getCode());
        orderPayExample.or().andChannelTimeBetween(dateStart, dateEnd)
                .andStatusEqualTo(OrderPayStatusEnum.PART_REFUND.getCode())
                .andChannelEqualTo(OrderPayChannelEnum.WXPAY.getCode())
                .andBillStatusNotEqualTo(BillStatusEnum.NORMAL.getCode());
        orderPayExample.or().andChannelTimeBetween(dateStart, dateEnd)
                .andStatusEqualTo(OrderPayStatusEnum.REFUND.getCode())
                .andChannelEqualTo(OrderPayChannelEnum.WXPAY.getCode())
                .andBillStatusNotEqualTo(BillStatusEnum.NORMAL.getCode());
        List<OrderPay> orderPayList = orderPayMapper.selectByExample(orderPayExample);

        for (OrderPay orderPayDB : orderPayList) {
            LOG.info("支付订单数据：{}", orderPayDB);
            String payNo = orderPayDB.getPayNo();
            BillWxpayExample billWxpayExample = new BillWxpayExample();
            billWxpayExample.createCriteria().andPayNoEqualTo(payNo)
                    .andChannelTimeBetween(dateWxPayStart, dateWxPayEnd)
                    .andChannelStatusEqualTo("SUCCESS");
            List<BillWxpay> billWxpayList = billWxpayMapper.selectByExample(billWxpayExample);
            if (CollectionUtils.isEmpty(billWxpayList)) {
                LOG.warn("对账结果短款：{}", orderPayDB);
                billHandleService.doWrongBillWxPay(billBatch, orderPayDB, null, BillStatusEnum.SHORT.getCode());
            } else {
                BillWxpay billWxpayDB = billWxpayList.get(0);
                LOG.info("查询到的微信对账数据：{}", billWxpayDB);
                if (orderPayDB.getChannelAmount().compareTo(billWxpayDB.getAmountIn()) == 0) {
                    LOG.info("对账结果正常，payNo：{}", payNo);
                    billHandleService.doRightBillWxPay(billBatch, orderPayDB, billWxpayDB);
                } else {
                    LOG.warn("对账结果金额错误：{}", orderPayDB);
                    billHandleService.doWrongBillWxPay(billBatch, orderPayDB, billWxpayDB, BillStatusEnum.ERROR.getCode());
                }
            }
        }
    }

    private void checkBillWxPayB(String billDateStr, BillBatch billBatch) {
        Date dateStart = DateTimeUtil.parse(billDateStr, "yyyy-MM-dd");
        Date dateOrderPayStart = DateTimeUtil.afterDays(dateStart, -10);
        Date dateOrderPayEnd = DateTimeUtil.afterDays(dateStart, 10);

        BillWxpayExample billWxpayExample = new BillWxpayExample();
        billWxpayExample.createCriteria().andBatchNoEqualTo(billBatch.getBatchNo())
                .andChannelStatusEqualTo("SUCCESS")
                .andBillStatusNotEqualTo(BillStatusEnum.NORMAL.getCode());
        List<BillWxpay> billWxpayList = billWxpayMapper.selectByExample(billWxpayExample);

        for (BillWxpay billWxpayDB : billWxpayList) {
            LOG.info("微信订单数据：{}", billWxpayDB);
            String payNo = billWxpayDB.getPayNo();
            OrderPayExample orderPayExample = new OrderPayExample();
            orderPayExample.or().andPayNoEqualTo(payNo)
                    .andStatusEqualTo(OrderPayStatusEnum.SUCCESS.getCode())
                    .andChannelTimeBetween(dateOrderPayStart, dateOrderPayEnd);
            orderPayExample.or().andPayNoEqualTo(payNo)
                    .andStatusEqualTo(OrderPayStatusEnum.PART_REFUND.getCode())
                    .andChannelTimeBetween(dateOrderPayStart, dateOrderPayEnd);
            orderPayExample.or().andPayNoEqualTo(payNo)
                    .andStatusEqualTo(OrderPayStatusEnum.REFUND.getCode())
                    .andChannelTimeBetween(dateOrderPayStart, dateOrderPayEnd);
            List<OrderPay> orderPayList = orderPayMapper.selectByExample(orderPayExample);
            if (CollectionUtils.isEmpty(orderPayList)) {
                LOG.warn("对账结果长款：{}", billWxpayDB);
                billHandleService.doWrongBillWxPay(billBatch, null, billWxpayDB, BillStatusEnum.LONG.getCode());
            } else {
                OrderPay orderPayDB = orderPayList.get(0);
                LOG.info("查询到的本地订单对账数据：{}", orderPayDB);
                if (orderPayDB.getChannelAmount().compareTo(billWxpayDB.getAmountIn()) == 0) {
                    LOG.info("对账结果正常，payNo：{}", payNo);
                    billHandleService.doRightBillWxPay(billBatch, orderPayDB, billWxpayDB);
                } else {
                    LOG.warn("对账结果金额错误：{}", orderPayDB);
                    billHandleService.doWrongBillWxPay(billBatch, orderPayDB, billWxpayDB, BillStatusEnum.ERROR.getCode());
                }
            }
        }
    }
}
